#include "SwapBinopArgsPass.h"

#include "llvm/IR/InstIterator.h"
#include "llvm/Passes/PassPlugin.h"

void SwapBinopArgsPass::registerCallbacks(llvm::PassBuilder &PB) {
  PB.registerPipelineParsingCallback(
      [](llvm::StringRef Name, llvm::FunctionPassManager &PM,
         llvm::ArrayRef<llvm::PassBuilder::PipelineElement> InnerPipeline) {
        if (Name == "swap-bin-args") {
          PM.addPass(SwapBinopArgsPass());
          return true;
        }
        return false;
      });
}

// Основная ф-ция, реализующая логику прохода.
llvm::PreservedAnalyses SwapBinopArgsPass::run(
  llvm::Function &F, llvm::FunctionAnalysisManager &AM)
{
  bool Changed = false;
  for (llvm::inst_iterator I = llvm::inst_begin(F), E = llvm::inst_end(F); I != E; ++I) {
    if (auto *BinOp = llvm::dyn_cast<llvm::BinaryOperator>(&*I)) {
      auto *Sav = BinOp->getOperand(1);
      BinOp->setOperand(1, BinOp->getOperand(0));
      BinOp->setOperand(0, Sav);
      Changed = true;
    }
  }
  if (Changed) {
    // Изменения были. Они точно не поменяли CFG, по этому, по крайней мере, наш
    // проход не повлиял на анализы уровня CFG.
    llvm::PreservedAnalyses PA;
    PA.preserveSet<llvm::CFGAnalyses>();
    return PA;
  }
  // Изменений не было - рапортуем, что мы не меняли правильность никаких анализов.
  return llvm::PreservedAnalyses::all();
}

// Интерфейс для сторонних инструментов (типа clang, opt).
// Они ищут и запускают данную ф-цию при загрузке дин. библиотеки плагина для
// получения инф-ции о доступных проходах.
extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo
llvmGetPassPluginInfo() {
  return {LLVM_PLUGIN_API_VERSION, "toy-plugin", "0.1",
          SwapBinopArgsPass::registerCallbacks};
}
